查看原文
其他

【冬瓜哥熊文】大话流水线(1)~流水线基础理论

冬瓜哥 大话存储 2018-10-31

本期开始,冬瓜哥将分三次为大家全面介绍流水线有关的知识。


        试想两个人在接力搬东西,如果这两个人的速度能保持完全一样,那么配合会非常完美,我左手拿东西传到右手,你左手刚好空出来拿到我右手递给你的东西。但是突然你感觉头上痒的不行了,去挠了一下,这下好了,我就得暂停,等你挠完了再继续。此时我多么想你跟前有个篮子啊,这样我就可以放在篮子里,你爱挠哪挠哪,挠完了你自己从篮子里拿走。

对啊,程序员也是这么想的。两个设备之间、两个程序之间,要想达到高吞吐量,就得这样将信息的传递异步化,而不是同步化。

        传递东西有两种方式:

        A.  同步等停方式。源头某角色生成一样物品,然后让第一个人用左手从源头拿走,传递给他的右手,传递给第二个人的左手,东西传给第二个人之后,第一个人不做任何动作,纯等待源头再交给他另一样物品。

想象一下,如果整个传递路径上只有两个人还算好,如果有10个人,可以想象,这样物品从源头传递到目的端所需要的时间将会非常长,而在这段时间内,源头不会再传递任何物品,这10个人中总有9个人闲着没事。一样物品从第一个人传递到最后一个人所经历的时间,被称为这条传递链的时延/延迟。假设每个人从拿到物品到传递给下一个人,需要1ms的时间,那么由10个人组成的传递链,整个传递链从头传递一次就需要10ms的时间(该传递链条的时延=10ms),那么这条传递链每秒可以传递1000ms/10ms=100个物品,也就是100物品/s,这就是该传递链的吞吐量

问一下:同步模式下,如何增加传递链的吞吐量?答案似乎只有一个:降低传递链的总时延。如何降低呢?要么提高每个人的处理速度,要么砍掉不必要的人。

人们天然的会想到,能否让源头源源不断的将物品传递个第一个人,第一个人也源源不断的传递给第二个人,以此类推。于是有了异步方式。

 

        B.  异步流水线方式。源头让第一个人左手拿走一个物品,第一个人把物品传递到自己右手后,马上再从源头拿一样物品。一瞬间,第一个人左手和右手同时拿着两个物品。当第二个人结果第一个物品后,第一个人左手再将第二个物品传递给右手,左手空出,可以再从源头拿第三个物品。第二个人向第三个人传递时也这样去做。这就是异步传递模式。问一下:这种传递模式下,一样物品从源头到目的,经历了多长时间?当然还是10ms,没的说,也就是说,传递链总时延并没有变化,每样物品从源头传递到目的依然还是10ms。答对了,加十分。

问一下:此时该传递链每秒能传递多少样物品?这问题得分析一下,第一样物品从源头传递到目的当然需要10ms,但是第二样物品在第一样物品到达之后的1ms(最后一个人从左手传到右手的时间)也到达了,同理,后续所有的物品都是相隔1ms间距,一个接一个的到达了。那么就可以算出来在1000ms内,头10ms传递了一样物品,后990ms每1ms可以传递一样物品,这样的话,吞吐量变为990+1=991物品/s。吞吐量几乎提升了10倍!

问一下:在这个基础上,想进一步提升吞吐量,共有几种方式?自然,第一种方式是降低每个人从左手传递到右手的时间;这样最见效,比如降低到0.5ms,则吞吐量将为:1+(1000-5)/0.5=1991物品/s。第二种则是减少传递链上的人的数量,这样可以将第一个物品所需的10ms降低,比如,降低到2个人,那么吞吐量将为1+(1000-2)/1=998物品/s,似乎提升并不是很大。第三种则是再增加一条或者多条传递链,多条一起传递,那直接性能翻对应的倍数。答对了,加十分。

问一下:是不是可以说,在异步传递模式下,传递链的总体时延对吞吐量影响并不大?是的。答对了加五分。

问一下:既然传递每样物品需要10ms,那么每秒能传递1000ms/10ms=100样物品啊,这好像是小学数学应用题中最简单的那一道啊,为什么上面算出来的却是991样/s呢?哪里出了问题?仔细想来,还真有点烧脑。其实这里的关键点在于,同一时刻内,有多个物品在同时并行向前传递,传递链中有几个人在接力,传递持续稳定之后,同一时刻就有几样物品在传递。所以,上述例子中,并发度为10,忽略第一个物品传递时一段时间内并行度没有达到10,所以最终吞吐量的确是100×10=1000物品/s。准确来讲应该是:吞吐量=并行度×1000ms/节点时延。神奇啊!

问一下:如果传递链上的每个人都是各色人等,其各自从左手传递到右手的时间都不同,有快有慢。最终吞吐量是怎么个情况?这可真有点难以用脑子想清楚,得画一下,算一下才行。

        先看看上图左边的情况,传递链两头是俩壮汉,中间夹了一位老爷爷和一位小朋友。很显然,当第一个物品传递到目的之后,第二个物品需要等待40个时间单位才能到达目的,这是不是说明,后续每个物品都会以40个时间单位为间隔陆续到达呢?可以明确推出,是的。那么是不是可以有这样一个结论:后续每个物品的到达间隔统一为传递链中耗时最长的那个链条节点所耗费的时间呢?为了进一步证明该问题,我们把传递链右侧的壮汉换成一位老奶奶,则的确,第二个物品会在第一个物品之后的50个时间单位到达,后续其他物品也都会以相隔50个时间单位的间隔到达。

        结论已经非常明确,可以明显看到,只要传递链中有处理比较慢的节点,其他节点的处理速度再快也是没用的,处理完了也只能原地等待。也就是说,即使把上面两条传递链里每个人分别都换成老爷爷/老奶奶,最终得到的吞吐量也是一样的。

 

问一下:假设传递链中有一个节点时延为40个时间单位,其他所有节点都为10个时间单位,那么如果将产生40个时间单位时延的这个人的位置上原地替换为3个10ms时延的人,吞吐量会不会有改善?当然有改善,吞吐量会提升40/10=4倍。这有点神奇了,人多了,吞吐量反而上来了。 

问一下:有一条由10个人组成的传递链,每个人的处理时延是10ms。现在,将其更换为由20个人组成的传递链,而每个人的处理时延为5ms,吞吐量如何变化?根据上文中的结论,可以推断出,吞吐量提升10/5=2倍,同时总时延不变。

问一下:替换为40个人,每人处理时延依然为5ms,相对20人每人5ms的传递链,吞吐量如何变化?可以看到,除了第一个物品会以200ms的时间传过来之外,后续物品依然是以5ms为间隔到达,吞吐量与20人每人5ms的传递链保持一致。

 

异步模式吞吐量公式:吞吐量=1/max[每个节点的时延]

同步模式吞吐量公式:吞吐量=1/sum[所有节点的时延]

同步/异步模式时延公式:总时延=sum[所有节点的时延]


        可以看到,只要将某个物品的全部处理流程细分为若干个小流程,让每一步小流程很快的完成,这样就可以组成一条拥有极高吞吐量的传递链了。


         流水线,就是指上述的异步传递过程。只不过传递链上的每个角色需要对物品做对应的处理而不是单纯的传递。比如第一个人负责把物品做某种装饰,第二个人负责对物品进行盖章,第三个人负责用一张大包装纸对物品进行包装。这就是一条产品加工流水线。整个流水线中工序数量被称为流水线的级数,本例这条产品包装流水线为3级流水线。

        可以想象,第一步是最耗时的,需要在多个地方贴上对应的装饰品,假设需要10秒钟,第二步只是盖个章,假设需要1秒钟,第三步需要包起来,假设需要3秒钟。很显然,要让这条流水线的产量提升的话,必须将第一步分解成3步,每一步只负责贴部分装饰,假设耗时3秒,这样就可以与第三步的时延匹配起来。能否继续优化?也就是将每一步的时延降低为1秒钟?第一步可以,用10个人,每个人只耗费1秒钟贴一个装饰品即可。但是最后一步恐怕不能再细分了,除非用机器,因为靠人工的话,包装过程不可能被细分为比如第一步只折一个角,第二步再折另外一个角,因为当传递给下一步时,上一步折的角很可能已经自动松开了。这就是现实的无奈,最后一步会成为瓶颈。

        解决这个问题的办法,就是找三个人来并行完成最后一步,也就是让每人都处理一个物品,虽然每个人依然用3秒钟包装一个物品,但是3秒钟内却可以同时包装3个物品,这样的话就等价于每一步时延都为1秒时的吞吐量了。上述方式是物理上的可直观感知的并发,而且真的是多个物品齐头并进,可以称之为多路物理并发模式;而多个人组成异步传递链产生的并发传递,也是并发,只不过理解起来困难一些,可称之为多级流水线并发模式,并且多个物品之间并非齐头并进,而是有一定先后,相隔的时间就是max[每个节点的时延]。 

        使用4级流水线并发和4路物理并发获取的吞吐量是相同的,只是方式有区别,前者是每10s出一个(每40s出4个),后者是每40s出4个。物理并发方式下,当第一轮传递开始之后的40s,会有4个物品传出;而4级流水线并发模式下,传递开始后40s却只有1个物品被传出。

        从该图可以看出,多路物理并发模式的起跑天然比流水线模式要快,但是跑起来之后,两者的速度是相同的,实际中可以忽略这个起跑差异,毕竟这并不是比赛。我们把第一个物品从流水线进入到传出的过程叫做入流水阶段,此过程中,流水线会被充满,一旦充满,流水线就可以全速运行,也就是全并行阶段。正是因为流水线必须先被充满之后才能全并行,所以导致了其比物理并发模式起跑慢。

        另外,这两个模式之间还有一个微妙的事情。假设该步骤的下游还有其他步骤的话,除非下游的步骤也是物理并发的,否则上一步的物理并发产生的起跑超前效应将会在下游步骤被屏蔽掉。如下图所示,一股脑先到达了目的,倒头来还得一个一个的经过下游步骤的处理,最终物理并发模式和多级流水线并发模式产生的吞吐量依然相同,同时每个物品到达时刻点也完全相同了。这好像龟兔赛跑,乌龟虽然一步一步往前挪,但是最后反而和兔子一起到达了终点。

提示

谁在乎这“可以忽略”的起跑超前效应?比如有一类场景是金融领域的高频交易,交易者必须在股票或者某种金融产品上涨或者下跌一定数额(通常是极微量的增幅)之后立即发起并完成交易,因为有大量的交易者在排队,谁先第一时间从证券交易所服务器上获知对应的涨幅并且将交易请求尽快发送到交易服务器,谁的交易请求就能排在队列前面,就可以抢先卖出或者买入。而交易请求,就像是一样物品,会在多个角色之间传递,有一条传递链存在。但是这类交易往往是同步模式居多,也就是发送一条消息,等待对方服务器返回确认消息之后,再发送下一条消息。

 

问一下:对于下游步骤没有物理并发的场景,如果将上游步骤的物理并发度提升一下,比如从4提升到8,会不会有收益?见下图,很显然,没有任何作用。

问一下:如果在下游非并发步骤之后再增加一个并发步骤,会不会有什么收益?根据下图,很显然,没有任何收益。

问一下,如果上述例子中将上面传递链的第一级时延降低到与第二条传递链第一级时延相同,会不会有区别?根据下图所示,吞吐量没有区别。 

问一下:两条传递链,级数相同,每一级时延相同,但是第一条传递链上第一级4路并发,第二级2路并发,第三级4路并发。这两条传递链的吞吐量相比有什么差异?根据下图可判断,第一条传递链的吞吐量为第二条的2倍而不是4倍。也就是说,并发度最小的那一级决定了整个传递链的物理并发度。

        根据对上几问的分析,最终可以有这个结论:异步传递链上每一级的吞吐量公式为:

  • 当上级时延=下级时延时,吞吐量=min[物理并发度]/时延

  • 当上级时延<下级时延时:

  • 当(下级并发度/上级并发度)≤(下级时延/上级时延)时,吞吐量=下级并发度/下级时延

  • 当(下级并发度/上级并发度)>(下级时延/上级时延)时,吞吐量=上级并发度/上级时延

  • 当上级时延>下一级时延时:

  • 当上级并发度≤下级并发度时,吞吐量=上级并发度/上级时延

  • 当(上级并发度/下级并发度)<(上级时延/下级时延)时,吞吐量=上级并发度/上级时延

  • 当(上级并发度/下级并发度)>(上级时延/下级时延)时,吞吐量=下级并发度/下级时延

  • 当(上级并发度/下级并发度)=(上级时延/下级时延)时,吞吐量=上级并发度/上级时延或者下级并发度/下级时延


异步传递链的总吞吐量公式为:吞吐量=min[每一级的吞吐量]。


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存